﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.Remoting;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using PicStore.BLL.Impl;
using PicStore.BLL.Interface;
using PicStore.Common.Ioc;
using PicStore.Model;
using PicStore.UploaderInterface;
using PicStore.Common.Log;

namespace PicStore.Client
{
    public partial class Upload : Form
    {
        private ICategoryService categoryService = ObjectFactory.GetInstance<ICategoryService>();
        private IPictureService pictureService = ObjectFactory.GetInstance<IPictureService>();
        private ILog log = ObjectFactory.GetInstance<ILog>();

        private List<PictureInfoForUpload> topListForUpload = null;
        private List<PictureInfoForUpload> bottomListForUpload = null;
        private List<PictureInfoForUpload> coordiListForUpload = null;
        private int uploaderId = 0;
        public Upload(int userId)
        {
            uploaderId = userId;
            InitializeComponent();
            lblResult.Text = string.Empty;
        }

        private void btnBrowse_Click(object sender, EventArgs e)
        {
            BrowseFile();
        }

        private void BrowseFile()
        {
            var dialogResult = openFileDialogForExcel.ShowDialog();
            if (dialogResult == DialogResult.OK)
            {
                txtExcelPath.Text = openFileDialogForExcel.FileName;
                txtResult.Text = string.Empty;
                btnConfirm.Visible = false;
                btnUpload.Visible = true;
            }
        }

        private void btnUpload_Click(object sender, EventArgs e)
        {
            RemotingConfiguration.Configure(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, false);
            var server = (IUploader)Activator.GetObject(typeof(IUploader), ConfigurationManager.AppSettings["RemotingUrl"]);
            try
            {
                bool isActive = server.IsServerActive();
            }
            catch (Exception exception)
            {
                MessageBoxButtons messButton = MessageBoxButtons.OK;
                DialogResult dr = MessageBox.Show("Remote server is not active, please contact administrator!", "Quit", messButton);
                log.Severe("Upload files failed.", exception);
                return;
            }

            btnConfirm.Visible = false;
            lblError.Text = string.Empty;
            if (string.IsNullOrEmpty(txtExcelPath.Text))
            {
                lblError.Text = "请选择一个Excel文件";
                return;
            }

            // 获取上传的Table
            string excelError = null;
            string excelDate = null;
            DataTable topExcelTable = ExcelHelper.ExcelToDT(txtExcelPath.Text, "TOP", out excelDate,
                out excelError); //把上传成功的excel文件读取其中的内容
            string path = Path.GetDirectoryName(txtExcelPath.Text);
            if (!(string.IsNullOrEmpty(excelError) && topExcelTable.Rows.Count != 0))
            {
                lblError.Text = "Excel文件内容出错！" + excelError;
                return;
            }

            DataTable bottomExcelTable = ExcelHelper.ExcelToDT(txtExcelPath.Text, "BOTTOM", out excelDate, out excelError); //把上传成功的excel文件读取其中的内容
            DataTable coordiExcelTable = ExcelHelper.ExcelToDT(txtExcelPath.Text, "COORDI", out excelDate, out excelError); //把上传成功的excel文件读取其中的内容

            // 如果出错，则报出行号。以及出错所在列。
            // 每个出错的行和列都要标明出来。
            bool canContinue = false;
            topListForUpload = GetPictureInfoFromExcel(topExcelTable, (int)PictureType.TOP, path, out canContinue);
            if (canContinue)
            {
                bottomListForUpload = GetPictureInfoFromExcel(bottomExcelTable, (int)PictureType.BOTTOM, path, out canContinue);
            }
            if (canContinue)
            {
                coordiListForUpload = GetPictureInfoFromExcel(coordiExcelTable, (int)PictureType.COORDI, path, out canContinue);
            }

            StringBuilder result = new StringBuilder();
            if (canContinue == false)
            {
                result.AppendFormat("Result of {0} sheet:", PictureType.TOP.ToString()).AppendLine();
                foreach (var picture in topListForUpload)
                {
                    if (picture.IsValidated == false)
                    {
                        result.AppendFormat("The {0} line, error: {1}", picture.ExcelLineNumber, picture.ErrorMessage).AppendLine();
                    }
                }
                if (bottomListForUpload != null)
                {
                    result.AppendFormat("Result of {0} sheet:", PictureType.BOTTOM.ToString()).AppendLine();
                    foreach (var picture in bottomListForUpload)
                    {
                        if (picture.IsValidated == false)
                        {
                            result.AppendFormat("The {0} line, error: {1}", picture.ExcelLineNumber, picture.ErrorMessage).AppendLine();
                        }
                    }
                }
                if (coordiListForUpload != null)
                {
                    result.AppendFormat("Result of {0} sheet:", PictureType.COORDI.ToString()).AppendLine();
                    foreach (var picture in coordiListForUpload)
                    {
                        if (picture.IsValidated == false)
                        {
                            result.AppendFormat("The {0} line, error: {1}", picture.ExcelLineNumber, picture.ErrorMessage).AppendLine();
                        }
                    }
                }
            }
            else
            {
                result.AppendFormat("Result of {0} sheet:", PictureType.TOP.ToString()).AppendLine();
                foreach (var picture in topListForUpload)
                {
                    result.AppendFormat("Line {0}, Correct, picture name: {1}, place is {2}, ", picture.ExcelLineNumber, picture.PictureName, picture.Place).AppendLine();
                    result.AppendFormat("sex is: {0}, item is {1}, Color is {2}, ", picture.Sex, picture.Item, picture.Color);
                    result.AppendFormat("Style is:{0}, Length is: {1}, Collar is {2}, ", picture.Style, picture.Length, picture.Collar);
                    result.AppendFormat("Front is:{0}, Sleeve is: {1}, ", picture.Front, picture.Sleeve).AppendLine();
                    result.AppendFormat("SweaterTexture is {0}, Type is:{1},", picture.SweaterTexture, picture.Type).AppendLine();
                }
                result.AppendFormat("Result of {0} sheet:", PictureType.BOTTOM.ToString()).AppendLine();
                foreach (var picture in bottomListForUpload)
                {
                    result.AppendFormat("Line {0}, Correct, picture name: {1}, place is {2}, ", picture.ExcelLineNumber, picture.PictureName, picture.Place).AppendLine();
                    result.AppendFormat("sex is: {0}, item is {1}, Color is {2}, ", picture.Sex, picture.Item, picture.Color);
                    result.AppendFormat("Style is:{0}, Length is: {1}, Collar is {2}, ", picture.Style, picture.Length, picture.Collar);
                    result.AppendFormat("Front is:{0}, Sleeve is: {1}, ", picture.Front, picture.Sleeve).AppendLine();
                    result.AppendFormat("SweaterTexture is {0}, Type is:{1},", picture.SweaterTexture, picture.Type).AppendLine();
                }
                result.AppendFormat("Result of {0} sheet:", PictureType.COORDI.ToString()).AppendLine();
                foreach (var picture in coordiListForUpload)
                {
                    result.AppendFormat("Line {0}, Correct, picture name: {1}, place is {2}, ", picture.ExcelLineNumber, picture.PictureName, picture.Place).AppendLine();
                    result.AppendFormat("sex is: {0}, item is {1}, Color is {2}, ", picture.Sex, picture.Item, picture.Color);
                    result.AppendFormat("Style is:{0}, Length is: {1}, Collar is {2}, ", picture.Style, picture.Length, picture.Collar);
                    result.AppendFormat("Front is:{0}, Sleeve is: {1}, ", picture.Front, picture.Sleeve).AppendLine();
                    result.AppendFormat("SweaterTexture is {0}, Type is:{1},", picture.SweaterTexture, picture.Type).AppendLine();
                }
            }
            txtResult.Text = result.ToString();

            // 在检查没有错误的情况下，显示确定上传按钮。
            btnConfirm.Visible = canContinue;
            btnUpload.Visible = !btnConfirm.Visible;
        }

        protected List<PictureInfoForUpload> GetPictureInfoFromExcel(DataTable excelTable, int pictureType, string path,  out bool canContinue)
        {
            int startRowIndex = 0;
            canContinue = true;
            // 必填项没有，或者填写错误，就跳过，不作处理。
            for (int i = 0; i < excelTable.Rows.Count; i++)
            {
                string senderNameDescription = excelTable.Rows[i][0].ToString().Trim();
                if (senderNameDescription == ExcelHeader.Headers[0])
                {
                    startRowIndex = i + 1;
                    break;
                }
            }

            // 检测每一个上传的分类和value。确保取值范围正确。
            var collarValues = categoryService.GetItemsByCategoryId((int)BaseCategoryEnum.Collar).Select(c => c.ItemValue).ToList();
            var colorValues = categoryService.GetItemsByCategoryId((int)BaseCategoryEnum.Color).Select(c => c.ItemValue).ToList();
            var frontValues = categoryService.GetItemsByCategoryId((int)BaseCategoryEnum.Front).Select(c => c.ItemValue).ToList();
            var itemValues = categoryService.GetItemsByCategoryId((int)BaseCategoryEnum.Item).Select(c => c.ItemValue).ToList();
            var lengthValues = categoryService.GetItemsByCategoryId((int)BaseCategoryEnum.Length).Select(c => c.ItemValue).ToList();
            var placeValues = categoryService.GetItemsByCategoryId((int)BaseCategoryEnum.Place).Select(c => c.ItemValue).ToList();
            var sexValues = categoryService.GetItemsByCategoryId((int)BaseCategoryEnum.Sex).Select(c => c.ItemValue).ToList();
            var sleeveValues = categoryService.GetItemsByCategoryId((int)BaseCategoryEnum.Sleeve).Select(c => c.ItemValue).ToList();
            var styleValues = categoryService.GetItemsByCategoryId((int)BaseCategoryEnum.Style).Select(c => c.ItemValue).ToList();
            var sweaterTextureValues = categoryService.GetItemsByCategoryId((int)BaseCategoryEnum.SweaterTexture).Select(c => c.ItemValue).ToList();

            List<PictureInfoForUpload> list = new List<PictureInfoForUpload>();
            for (int i = startRowIndex; i < excelTable.Rows.Count; i++)
            {
                PictureInfoForUpload item = new PictureInfoForUpload();
                item.ExcelLineNumber = i + 1;
                item.IsValidated = true;
                item.ErrorMessage = string.Empty;

                #region 获取picture信息
                string fileName = excelTable.Rows[i][0].ToString().Trim();
                if (string.IsNullOrEmpty(fileName))
                {
                    // 文件名字为空的时候，即认为该列表已经结束了。
                    break;
                    //item.IsValidated = false;
                    //item.ErrorMessage = "PictureName cannot be null！";
                }
                if (fileName.ToLower().Contains(".jpg") == false)
                {
                    item.PictureName = string.Format(@"{0}\{1}.jpg", path, fileName);
                }
                else
                {
                    item.PictureName = string.Format(@"{0}\{1}", path, fileName);
                }

                if (File.Exists(item.PictureName) == false)
                {
                    item.IsValidated = false;
                    item.ErrorMessage = string.Format(@"Line {0}, Cannot find picture {1}", 
                        item.ExcelLineNumber, item.PictureName);
                }
                else
                {
                    FileInfo info = new FileInfo(item.PictureName);
                    int maxLength = 0;
                    int.TryParse(ConfigurationManager.AppSettings["MaxPictureLength"], out maxLength);
                    if (maxLength <= 0)
                    {
                        // 默认设置为300k
                        maxLength = 300000;
                    }
                    if (info.Length > maxLength)
                    {
                        item.IsValidated = false;
                        item.ErrorMessage = string.Format(@"Line {0}, Picture size cannot be larger than {1} bytes. ",
                            item.ExcelLineNumber, maxLength);
                    }
                }

                string dateString = string.Format("{0}-{1}-{2}", excelTable.Rows[i][1].ToString(),
                    excelTable.Rows[i][2].ToString(), excelTable.Rows[i][3].ToString());
                DateTime takePhotoDate = DateTime.Now;
                bool parseResult = DateTime.TryParse(dateString, out takePhotoDate);
                if (parseResult == false)
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Take photo date cannot be null！";
                }
                item.TakePhotoDate = takePhotoDate;

                string place = excelTable.Rows[i][4].ToString().Trim();

                // 去掉开头的0.
                if (place.StartsWith("0")) place = ExcelHelper.GetStringValueOfValue(place);
                if (string.IsNullOrEmpty(place))
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Place cannot be null！";
                }
                else if (placeValues.Contains(place) == false)
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Place value is invalid！";
                }
                item.Place = place;

                string sex = excelTable.Rows[i][5].ToString().Trim();
                // 去掉开头的0.
                if (sex.StartsWith("0")) sex = ExcelHelper.GetStringValueOfValue(sex);

                if (string.IsNullOrEmpty(sex))
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Sex cannot be null！";
                }
                else if (sexValues.Contains(sex) == false)
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Sex value is invalid！";
                }
                item.Sex = sex;

                string itemValue = excelTable.Rows[i][6].ToString().Trim();
                // 去掉开头的0.
                if (itemValue.StartsWith("0")) itemValue = ExcelHelper.GetStringValueOfValue(itemValue);

                if (string.IsNullOrEmpty(itemValue))
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Item cannot be null！";
                }
                else if (itemValues.Contains(itemValue) == false)
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Item value is invalid！";
                }
                item.Item = itemValue;

                string color = excelTable.Rows[i][7].ToString().Trim();
                // 去掉开头的0.
                if (color.StartsWith("0")) color = ExcelHelper.GetStringValueOfValue(color);

                if (string.IsNullOrEmpty(color))
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Color cannot be null！";
                }
                else if (colorValues.Contains(color) == false)
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Color value is invalid！";
                }
                item.Color = color;

                string style = excelTable.Rows[i][8].ToString().Trim();
                // 去掉开头的0.
                if (style.StartsWith("0")) style = ExcelHelper.GetStringValueOfValue(style);

                if (string.IsNullOrEmpty(style))
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Style cannot be null！";
                }
                else if (styleValues.Contains(style) == false)
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Style value is invalid！";
                }
                item.Style = style;

                string length = excelTable.Rows[i][9].ToString().Trim();
                // 去掉开头的0.
                if (length.StartsWith("0")) length = ExcelHelper.GetStringValueOfValue(length);

                if (string.IsNullOrEmpty(length))
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Length cannot be null！";
                }
                else if (lengthValues.Contains(length) == false)
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Length value is invalid！";
                }
                item.Length = length;

                string collar = excelTable.Rows[i][10].ToString().Trim();
                // 去掉开头的0.
                if (collar.StartsWith("0")) collar = ExcelHelper.GetStringValueOfValue(collar);

                if (string.IsNullOrEmpty(collar))
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Collar cannot be null！";
                }
                else if (collarValues.Contains(collar) == false)
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Collar value is invalid！";
                }
                item.Collar = collar;

                string front = excelTable.Rows[i][11].ToString().Trim();
                // 去掉开头的0.
                if (front.StartsWith("0")) front = ExcelHelper.GetStringValueOfValue(front);

                if (string.IsNullOrEmpty(front))
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Front cannot be null！";
                }
                else if (frontValues.Contains(front) == false)
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Front value is invalid！";
                }
                item.Front = front;

                string sleeve = excelTable.Rows[i][12].ToString().Trim();
                // 去掉开头的0.
                if (sleeve.StartsWith("0")) sleeve = ExcelHelper.GetStringValueOfValue(sleeve);

                if (string.IsNullOrEmpty(sleeve))
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Sleeve cannot be null！";
                }
                else if (sleeveValues.Contains(sleeve) == false)
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "Sleeve value is invalid！";
                }
                item.Sleeve = sleeve;

                string sweaterTexture = excelTable.Rows[i][13].ToString().Trim();
                // 去掉开头的0.
                if (sweaterTexture.StartsWith("0")) sweaterTexture = ExcelHelper.GetStringValueOfValue(sweaterTexture);

                if (string.IsNullOrEmpty(sweaterTexture))
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "SweaterTexture cannot be null！";
                }
                else if (sweaterTextureValues.Contains(sweaterTexture) == false)
                {
                    item.IsValidated = false;
                    item.ErrorMessage = "SweaterTexture value is invalid！";
                }
                item.SweaterTexture = sweaterTexture;

                item.Type = pictureType.ToString();

                if (item.IsValidated == false)
                {
                    canContinue = false;

                    // 加入list，显示错误给用户看到。
                    list.Add(item);
                    break;
                }
                #endregion

                list.Add(item);
            }

            return list;
        }


        private void txtExcelPath_Click(object sender, EventArgs e)
        {
            BrowseFile();
        }

        private void btnConfirm_Click(object sender, EventArgs e)
        {
            if (topListForUpload == null)
            {
                return;
            }

            RemotingConfiguration.Configure(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, false);
            var server = (IUploader)Activator.GetObject(typeof(IUploader), ConfigurationManager.AppSettings["RemotingUrl"]);
            try
            {
                bool isActive = server.IsServerActive();
            }
            catch (Exception exception)
            {
                MessageBoxButtons messButton = MessageBoxButtons.OK;
                DialogResult dr = MessageBox.Show("Remote server is not active, please contact administrator!", "Quit", messButton);
                log.Severe("Upload files failed.", exception);
                return;
            }

            /// 上传操作，实现为同步操作。
            ///  1. 逐个文件检查，逐个文件上传，逐个图片信息保存到数据库。
            ///  2. 同时把每个文件的操作结果打印到界面上。
            ///  3. 然后把每个文件的操作结果，写入一个log文件。供回头查找。

            
            StringBuilder errorMessage = new StringBuilder();
            RemotingConfiguration.Configure(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, false);
            var uploader = (IUploader)Activator.GetObject(typeof(IUploader), ConfigurationManager.AppSettings["RemotingUrl"]);

            string path = Path.GetDirectoryName(txtExcelPath.Text);
            string successFolder = path + @"\successPictures\";
            if (Directory.Exists(successFolder) == false)
            {
                Directory.CreateDirectory(successFolder);
            }
            string failedFolder = path + @"\failed\";
            if (Directory.Exists(failedFolder) == false)
            {
                Directory.CreateDirectory(failedFolder);
            }

            bool topListHasError = UploadFileList(uploader, errorMessage, successFolder, failedFolder, topListForUpload);
            bool bottomListHasError = UploadFileList(uploader, errorMessage, successFolder, failedFolder, bottomListForUpload);
            bool coordiListHasError = UploadFileList(uploader, errorMessage, successFolder, failedFolder, coordiListForUpload);

            btnConfirm.Visible = false;
            string logFile = string.Format(@"{0}\UploadLog.txt", path);
            using (StreamWriter file = new StreamWriter(logFile))
            {
                file.Write(errorMessage.ToString());
            }
            if (topListHasError || bottomListHasError || coordiListHasError)
            {
                lblResult.Text = "There are error during upload. ";
            }
            else
            {
                lblResult.Text = "Upload all completed. ";
            }

            errorMessage.AppendFormat(" log file: {0} ", logFile);
            txtResult.Text = errorMessage.ToString();
        }

        private bool UploadFileList(IUploader uploader, StringBuilder errorMessage, string successFolder, 
                                    string failedFolder, List<PictureInfoForUpload> list)
        {
            bool hasError = false;
            foreach (var picture in list)
            {
                try
                {
                    bool uploadSuccess = false;
                    string serverPictureName = string.Empty;
                    string relativePathName = string.Empty;
                    string error = string.Empty;
                    string onlyFileName = Path.GetFileName(picture.PictureName);

                    bool isCurrentFileExist = File.Exists(picture.PictureName);
                    if (isCurrentFileExist)
                    {
                        // Upload file to server.
                        FileStream stream = File.OpenRead(picture.PictureName);
                        byte[] buffer = new byte[stream.Length];
                        stream.Read(buffer, 0, buffer.Length);
                        stream.Close();
                        uploadSuccess = uploader.SendFile(buffer, onlyFileName, picture.TakePhotoDate, out serverPictureName,
                                                   out relativePathName);

                        if (uploadSuccess == false)
                        {
                            // 如果错误，则重试3次。(次数可以配置。)
                            for (int i = 0; i < 3; i++)
                            {
                                uploadSuccess = uploader.SendFile(buffer, onlyFileName, picture.TakePhotoDate, out serverPictureName,
                                                           out relativePathName);
                                if (uploadSuccess)
                                {
                                    break;
                                }
                            }
                        }

                        if (uploadSuccess == false)
                        {
                            error = string.Format("Cannot upload file {0} to server.", picture.PictureName);
                        }
                    }
                    else
                    {
                        // 可能已经被上传过了。检查该文件是否已经上传过了，如果是，那就没必要再次上传了。
                        uploadSuccess = pictureService.IsPictureUploaded(onlyFileName, out serverPictureName, out relativePathName);

                        if (uploadSuccess == false)
                        {
                            error = string.Format("Cannot find file {0}.", picture.PictureName);
                        }
                    }

                    string newFileName = string.Empty;
                    if (uploadSuccess)
                    {
                        // record the picture info into the database.
                        pictureService.CreatePicture(uploaderId, serverPictureName, onlyFileName, relativePathName,
                                                     picture.TakePhotoDate,
                                                     picture.Place, picture.Sex, picture.Item, picture.Color, picture.Style,
                                                     picture.Length, picture.Collar,
                                                     picture.Front, picture.Sleeve, picture.SweaterTexture, picture.Type);

                        errorMessage.AppendLine(string.Format("Line {0}: Upload file {1} success.", picture.ExcelLineNumber,
                                                              picture.PictureName));
                        newFileName = string.Format("{0}{1}", successFolder, onlyFileName);
                    }
                    else
                    {
                        string message =
                            string.Format(
                                "Line {0} error: {1}.  Please contact administrator.",
                                picture.ExcelLineNumber, error);

                        errorMessage.AppendLine(message);
                        hasError = true;

                        newFileName = string.Format("{0}{1}", failedFolder, onlyFileName);
                    }

                    if (isCurrentFileExist)
                    {
                        // 完成上传，或者上传错误，都要把文件移到指定的文件夹。
                        if (File.Exists(newFileName)) File.Delete(newFileName);
                        File.Move(picture.PictureName, newFileName);
                    }
                }
                catch (Exception exception)
                {
                    // 所有留在原文件夹的图片，都一定是上传过程中发生异常的图片。
                    string message = "Line {0}, exception occurs.";
                    log.Severe(message, exception);

                    errorMessage.AppendLine(message + exception.ToString());
                }
            }

            return hasError;
        }

        private void Upload_FormClosing(object sender, FormClosingEventArgs e)
        {
            MessageBoxButtons messButton = MessageBoxButtons.OKCancel;
            DialogResult dr = MessageBox.Show("Are you sure to quit the system?", "Quit", messButton);
            e.Cancel = (dr != DialogResult.OK);
        }

        private void Upload_FormClosed(object sender, FormClosedEventArgs e)
        {
            Application.Exit();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.Hide();
            Update updater = new Update(uploaderId);
            updater.ShowDialog();
        }
    }
}